package cn.dev8.sdk.kotlin

import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.ui.Messages
import nbcp.base.comm.const
import nbcp.base.extend.HasValue
import nbcp.base.extend.Repeat
import nbcp.base.extend.Slice
import nbcp.base.extend.basicSame
import org.slf4j.LoggerFactory
import java.io.File

abstract class SwaggerAction : DumbAwareAction() {
    companion object {
        private val logger = LoggerFactory.getLogger(javaClass.declaringClass);
        private val keywordsInFunc =
            "break,case,try,catch,continue,do,if,else,finally,for,while,return,switch,this,throw,return".split(",")
    }

    override fun getActionUpdateThread(): ActionUpdateThread {
        return ActionUpdateThread.BGT
    }

    override fun update(e: AnActionEvent) {
        val project = e.getData(PlatformDataKeys.PROJECT)
        e.presentation.isEnabledAndVisible = project != null;
    }


    /**
     * 递归处理
     */
    protected fun walk(file: File): Int {
        var ret = 0;
        if (file.isDirectory) {
            for (subFile in file.listFiles()) {
                ret += walk(subFile);
            }
        } else {
            if (procJavaFile(file)) {
                ret++;
            }
        }

        return ret;
    }

    private fun List<String>.trimEmptyLines(): List<String> {
        var headIndex = this.indexOfFirst {

            var line = it.trim()

            if (line.isEmpty()) {
                return@indexOfFirst false;
            }

            if (line.startsWith("//")) {
                return@indexOfFirst false;
            }

            return@indexOfFirst true;
        }

        return this.Slice(headIndex);
    }


    /**
     * 处理
     */
    private fun procJavaFile(file: File): Boolean {
        if (!(file.extension basicSame "java")) {
            return false;
        }

        var lines = file.readLines().trimEmptyLines();

        var line1 = lines.getOrNull(0);
        if (line1 == null) {
            return false;
        }

        if (!line1.startsWith("package ")) {
            return false;
        }

        //如果是全注释类，则跳过。
        if (lines.all { it.isEmpty() || it.trim().startsWith("//") }) {
            return false;
        }


        var ret = procJavaFileContent(file);
        if (ret) {
            thisLogger().info("已处理: " + file.absolutePath);
        }

        return ret;
    }

    class JavaDocResult {
        var swagger = ""
        var startIndex = -1;
        var endIndex = -1;
    }


    private fun List<String>.endCommentIndex(startIndex: Int): Int {
        var index = startIndex;
        while (true) {
            index++;

            if (index >= this.size) break;
            var line = this[index].trim();
            if (line == "*/") {
                return index;
            }
        }
        return -1;
    }


    private fun List<String>.getDefLineIndex(startIndex: Int): Int {
        var index = startIndex;
        while (true) {
            index++;

            if (index >= this.size) break;
            var line = this[index].trim();
            if (line.isEmpty()) {
                continue
            }

            if (line.startsWith("@ApiModel")) {
                return -1;
            }
            if (line.startsWith("@ApiOperation")) {
                return -1;
            }
            if (line.startsWith("@ApiModelProperty")) {
                return -1;
            }

            if (line.startsWith("@")) {
                continue
            }

            if (line.startsWith("//")) {
                continue
            }

            //注释后再有注释？！
            if (line.startsWith("/*")) {
                return -1;
            }

            if (line.all { !it.isLetter() }) {
                return -1;
            }

            if (!line.first().isLetter()) {
                return -1;
            }

            if (!line.isClassDef() && !line.isMethoddDef() && !line.isFieldDef()) {
                return -1
            }

            return index;
        }
        return -1;
    }

    private fun String.isWord(): Boolean {
        return this.any() && this.first().isLetter() && this.all { it.isLetter() || it.isDigit() };
    }


    private fun String.isInvoke(): Boolean {
        if (this.startsWith("import ") ||
            this.startsWith("package ") ||
            this.startsWith("public ") ||
            this.startsWith("private ")
        ) {
            return false;
        }

        if (keywordsInFunc.any { this.startsWith(it) }) {
            return true;
        }

        if (this.endsWith("{")) {
            return true;
        }

        if (this.contains("=") ||
            this.contains(" catch ") ||
            this.contains(".")
        ) {
            return true;
        }

        if ("""^[\w\s\.]+\(""".toRegex().matches(this)) {
            return true
        }

        return false
    }

    private fun String.isFieldDef(): Boolean {
        var sect = this.split(" ").filter { it.HasValue };
        if (!sect.last().endsWith(";")) {
            return false;
        }

        if (this.isInvoke()) {
            return false;
        }

        sect = sect.Slice(0, -1) + sect.last().Slice(0, -1)
        return sect.all { it.isWord() };
    }

    private fun String.isClassDef(): Boolean {
        if (this.endsWith(";")) {
            return false;
        }

        if (this.isInvoke()) {
            return false;
        }

        return this.contains("class ")
    }

    private fun String.isMethoddDef(): Boolean {
        if (this.endsWith(";")) {
            return false;
        }

        if (this.isInvoke()) {
            return false;
        }

        //完整的函数定义
        var matchResult = """^\w*\s+\w+\s+\w+\s*\((.*)\)\s*$""".toRegex().find(this);
        if (matchResult != null) {

            var paramDef = matchResult.groups.get(1)?.value;
            if (paramDef == null) {
                return true;
            }

            return paramDef.split(",").all { def ->
                var sect = def.split(" ").filter { it.HasValue };
                if (sect.size == 2) {
                    return@all true;
                }

                //如果参数有注解
                if (sect.size == 3 && sect.get(0).startsWith("@")) {
                    return@all true
                }

                return@all false
            }
        }

        matchResult = """^\w*\s+\w+\s+\w+\s*\((.*),\s*$""".toRegex().find(this);
        if (matchResult != null) {

            var paramDef = matchResult.groups.get(1)?.value;
            if (paramDef == null) {
                return true;
            }

            return paramDef.split(",").all { def ->
                var sect = def.split(" ").filter { it.HasValue };
                if (sect.size == 2) {
                    return@all true;
                }

                //如果参数有注解
                if (sect.size == 3 && sect.get(0).startsWith("@")) {
                    return@all true
                }

                return@all false
            }
        }

        matchResult = """^\w*\s+\w+\s+\w+\s*\(\s*$""".toRegex().find(this);
        if (matchResult != null) {
            return true;
        }

        return false;
    }


    private fun List<String>.getPrevCodeLineIndex(endIndex: Int): Int {
        var index = endIndex;
        while (true) {
            index--;
            if (index <= 0) {
                return -1;
            }

            var line = this[index].trim();
            if (line.isEmpty()) {
                continue
            }
            if (line.startsWith("//")) {
                continue
            }
            if (line.startsWith("/*")) {
                continue
            }
            if (line.startsWith("*")) {
                continue
            }
            if (line.startsWith("*/")) {
                continue
            }

            return index; index;
        }

        return -1;
    }

    private fun List<String>.findJavaDocNoSwagger(): JavaDocResult {
        var index = -1;
        var ret = JavaDocResult();
        for (oriLine in this) {
            index++;
            if (index >= this.size) {
                break;
            }

            var lineTrim = oriLine.trim();

            if (lineTrim.isEmpty()) {
                continue
            }


            if (lineTrim.startsWith("//")) {
                var prevLineIndex = this.getPrevCodeLineIndex(index);
                if (prevLineIndex <= 0) {
                    continue
                }

                var prevLine = this.get(prevLineIndex).trim();
                if (prevLine.isInvoke()) {
                    continue
                }

                if (prevLine.startsWith("@")) {
                    continue;
                }

                var nextDefLineIndex = this.getDefLineIndex(index);
                if (nextDefLineIndex < 0) {
                    continue
                }

                var nextDefLine = this.get(nextDefLineIndex)
                var spaceLength = nextDefLine.indexOfFirst { it.isLetter() }
                nextDefLine = nextDefLine.trim();

                if (!nextDefLine.isFieldDef()) {
                    continue
                }

                var msg = lineTrim.Slice(2).trim()
                    .replace("\"", "“")
                    .replace("\\", "╲")
                    .replace("/", "╱")

                //认为是代码中的分隔线
                if (msg.startsWith("---")) {
                    continue
                }

                ret.startIndex = index;
                ret.endIndex = nextDefLineIndex - 1;

                ret.swagger = """${" ".Repeat(spaceLength)}@ApiModelProperty("${msg}")""";
                return ret;
            } else if (lineTrim.trim() == "/**") {
                var nextLine = this.get(index + 1).trim()
                if (!nextLine.startsWith("*")) {
                    continue
                }

                var msg = nextLine.Slice(1).trim()
                    .replace("\"", "“")
                    .replace("\\", "╲")
                    .replace("/", "╱")
                if (msg.isEmpty()) {
                    continue
                }

                if (msg.startsWith("@")) {
                    continue
                }

                ret.startIndex = index;
                ret.endIndex = this.endCommentIndex(index);


                //非@开头的下一行
                var defLineIndex = this.getDefLineIndex(ret.endIndex);
                if (defLineIndex == -1) {
                    continue
                }

                if (this.Slice(index + 1, defLineIndex).any {
                        var trimString = it.trim();
                        if (trimString.startsWith("@ApiModel")) {
                            return@any true;
                        }
                        if (trimString.startsWith("@ApiOperation")) {
                            return@any true;
                        }
                        if (trimString.startsWith("@ApiModelProperty")) {
                            return@any true;
                        }
                        return@any false;
                    }) {
                    continue
                }

                var defLine = this.get(defLineIndex)
                var spaceLength = defLine.indexOfFirst { it.isLetter() }

                defLine = defLine.trim();

                if (defLine.contains(" class ") || defLine.contains("enum ")) {
                    ret.swagger = """${" ".Repeat(spaceLength)}@ApiModel("${msg}")""";
                } else if (defLine.contains("(")) {
                    if (defLine.contains("static ")) {
                        continue
                    }
                    if (!this.Slice(index + 1, defLineIndex).any {
                            var trimString = it.trim();
                            if (trimString.startsWith("@GetMapping")) {
                                return@any true;
                            }
                            if (trimString.startsWith("@PostMapping")) {
                                return@any true;
                            }
                            if (trimString.startsWith("@PutMapping")) {
                                return@any true;
                            }
                            if (trimString.startsWith("@DeleteMapping")) {
                                return@any true;
                            }
                            if (trimString.startsWith("@RequestMapping")) {
                                return@any true;
                            }
                            return@any false;
                        }) {
                        continue
                    }
                    ret.swagger = """${" ".Repeat(spaceLength)}@ApiOperation("${msg}")""";
                } else {
                    ret.swagger = """${" ".Repeat(spaceLength)}@ApiModelProperty("${msg}")""";
                }
            } else {
                continue
            }

            return ret;
        }
        return JavaDocResult();
    }


    private fun procJavaFileContent(file: File, deepth: Int = 0): Boolean {
        var lines = file.readLines().trimEmptyLines();

        var result = lines.findJavaDocNoSwagger();
        if (result.endIndex < 0 || result.swagger.isEmpty()) {
            return false;
        }


        lines = lines.Slice(0, result.endIndex + 1) + result.swagger + lines.Slice(result.endIndex + 1);


        if (deepth == 0) {
            var import1 = "import io.swagger.annotations.ApiModelProperty;";
            var import2 = "import io.swagger.annotations.ApiModel;";
            var import3 = "import io.swagger.annotations.ApiOperation;";
            //找package后的第一个空行后的非空行
            var firstImportLineIndex = lines.indexOfFirst { it.startsWith("import ") };
            if (firstImportLineIndex < 0) {
                return false;
            }


            if (!lines.any { it == import1 }) {
                lines = lines.Slice(0, firstImportLineIndex) + import1 + lines.Slice(firstImportLineIndex);
            }
            if (!lines.any { it == import2 }) {
                lines = lines.Slice(0, firstImportLineIndex) + import2 + lines.Slice(firstImportLineIndex);
            }
            if (!lines.any { it == import3 }) {
                lines = lines.Slice(0, firstImportLineIndex) + import3 + lines.Slice(firstImportLineIndex);
            }
        }

        file.writeText(lines.joinToString(const.line_break))
        procJavaFileContent(file, deepth + 1);
        return true;
    }

}